#include "Channel.h"
#include "EventLoop.h"
#include "Logger.h"


#include <sys/epoll.h>

const int Channel::kNoneEvent = 0;
const int kReadEvent = EPOLLIN | EPOLLPRI; // EPOLLPRI:读事件中有紧急事件要处理
const int kWriteEvent = EPOLLOUT;

// EventLoop: ChannelList Poller
Channel::Channel(EventLoop *loop, int fd)
    : loop_(loop), fd_(fd), events_(0), revents_(0), tied_(false)
{
}

Channel::~Channel()
{
}

// 弱指针观察强指针
// Channel的tie方法什么时候调用过? 一个TcpConnection新连接创建的时候 TcpConnection => Channel
void Channel::tie(const std::shared_ptr<void> &obj)
{
    tie_ = obj;
    tied_ = true;
}

/*
 *  当改变Channel所表示fd的evevts事件后，update负责在Poller里面更改fd对应的事件epoll_ctl
 *  但是Channel和poller是两个类，Channel不能直接访问Poller
 *  EventLoop => ChannelList Poller,通过Eventloop调用Poller的函数
 */
void Channel::update()
{
    // 通过Channel所属的EventLoop，调用Poller的相应方法，注册fd的events事件
    loop_->updateChannel(this);
}

// 因为它不可能自己删自己，所以在Channel所属的EventLoop中，把当前的Channel删除掉
void Channel::remove()
{
    loop_->removeChannel(this);
}

// fd得到poller通知以后，处理事件的
void Channel::handleEvent(Timestamp receiveTime)
{
    if(tied_)
    {
        std::shared_ptr<void> guard = tie_.lock(); // 不是加锁，是把弱指针提升为强指针，避免了循环问题，还检测了对象存活检测
        // 若成功（返回非空 shared_ptr ），说明对象仍存在，可安全操作；若失败（返回空 ），说明对象已释放，避免访问悬空指针。
        if(guard)
        {
            handerEeventWithGuard(receiveTime);
        }
    }
    else
    {
        handerEeventWithGuard(receiveTime);
    }
}

// 根据poller通知Channel发送的具体事件，由Channel负责调用具体的回调操作
void Channel::handerEeventWithGuard(Timestamp receiveTime)
{
    LOG_INFO("Channel handleEvent revents:%d\n",revents_);

    if((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN)) // 出问题了
    {
        if(closeCallback_)
        {
            closeCallback_();
        }
    }

    if(revents_ & EPOLLERR)
    {
        if(errorCallback_)
        {
            errorCallback_();
        }
    }

    if(revents_ & (EPOLLIN | EPOLLPRI))
    {
        if(readCallbacke_)
        {
            readCallbacke_(receiveTime);
        }
    }

    if(revents_ & EPOLLOUT)
    {
        if(writeCallback_)
        {
            writeCallback_();
        }
    }
}
